package com.yarolegovich.slidingrootnav;

import com.yarolegovich.slidingrootnav.callback.DragListener;
import com.yarolegovich.slidingrootnav.callback.DragStateListener;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.LayoutScatter;

import java.util.ArrayList;
import java.util.List;

public class SlidingRootNavBuilder {
    private static final float DEFAULT_END_SCALE = 0.65f;
    private static final int DEFAULT_DRAG_DIST_DP = 150;
    private static final int DEFAULT_SLIDE_TIME = 150;      //单位为毫秒

    private Ability ability;
    private int menuLayoutRes;
    private Component menuComponent;
    private ComponentContainer contentView;
    private Component arbitrarilyComponent;
    private SlideGravity gravity;

    private boolean isMenuLocked;
    private Intent savedState;
    private boolean isMenuOpened;
    private boolean isContentClickableWhenMenuOpened;
    private int dragDistance;
    private float layoutScale;
    private int offsetDistance;

    private List<DragListener> dragListeners;
    private List<DragStateListener> dragStateListeners;

    public SlidingRootNavBuilder(Ability ability) {
        this.ability = ability;
        this.dragListeners = new ArrayList<>();
        this.dragStateListeners = new ArrayList<>();
        this.gravity = SlideGravity.LEFT;
        this.dragDistance = dpToPx(DEFAULT_DRAG_DIST_DP);
        this.isContentClickableWhenMenuOpened = true;
        this.layoutScale = DEFAULT_END_SCALE;
    }

    public SlidingRootNav inject() {
        ComponentContainer contentView = getContentView();

        Component oldRoot = contentView.getComponentAt(0);
        contentView.removeAllComponents();

        SlidingRootNavLayout newRoot = createAndInitNewRoot();
        Component menu = getMenuViewFor(newRoot);

        newRoot.addComponent(menu);
        newRoot.addComponent(oldRoot);

        contentView.addComponent(newRoot);
        newRoot.setSlideTime(DEFAULT_SLIDE_TIME);

        if (savedState == null && isMenuOpened) {
            newRoot.openMenu();
        }
        newRoot.setLayoutConfig();
        return newRoot;
    }

    public SlidingRootNavBuilder withMenuView(Component component) {
        menuComponent = component;
        return this;
    }

    public SlidingRootNavBuilder withMenuLayout(int layout) {
        menuLayoutRes = layout;
        return this;
    }

    private Component getMenuViewFor(SlidingRootNavLayout parent) {
        if (menuComponent == null) {
            if (menuLayoutRes == 0) {
                throw new IllegalStateException(ability.getString(ResourceTable.String_srn_ex_no_menu_view));
            }
            menuComponent = LayoutScatter.getInstance(ability).parse(menuLayoutRes, parent, false);
        }
        return menuComponent;
    }

    private ComponentContainer getContentView() {
        if (contentView == null) {
            ComponentParent componentParent = arbitrarilyComponent.getComponentParent();
            while (true) {
                ComponentParent tmp = componentParent.getComponentParent();
                if (tmp == null) {
                    break;
                } else {
                    componentParent = tmp;
                }
            }
            contentView = (ComponentContainer) componentParent;
        }
        return contentView;
    }

    private SlidingRootNavLayout createAndInitNewRoot() {
        SlidingRootNavLayout newRoot = new SlidingRootNavLayout(ability);
        newRoot.setHeight(-1);
        newRoot.setWidth(-1);
        newRoot.setGravity(gravity);
        newRoot.setMenuLocked(isMenuLocked);
        newRoot.setContentClickableWhenMenuOpened(isContentClickableWhenMenuOpened);
        newRoot.setMaxDragDistance(dragDistance);
        newRoot.setLayoutScale(layoutScale);
        newRoot.setOffsetDistance(offsetDistance);
        for (DragListener l : dragListeners) {
            newRoot.addDragListener(l);
        }
        for (DragStateListener l : dragStateListeners) {
            newRoot.addDragStateListener(l);
        }
        return newRoot;
    }

    /**
     * 增加Ability中任意一个Component
     *
     * @param component
     * @return SlidingRootNavBuilder
     */
    public SlidingRootNavBuilder withArbitrarilyView(Component component) {
        arbitrarilyComponent = component;
        return this;
    }

    public SlidingRootNavBuilder withGravity(SlideGravity g) {
        gravity = g;
        return this;
    }

    public SlidingRootNavBuilder withContentView(ComponentContainer cv) {
        contentView = cv;
        return this;
    }

    public SlidingRootNavBuilder withMenuLocked(boolean locked) {
        isMenuLocked = locked;
        return this;
    }

    public SlidingRootNavBuilder withSavedState(Intent state) {
        savedState = state;
        return this;
    }

    public SlidingRootNavBuilder withMenuOpened(boolean opened) {
        isMenuOpened = opened;
        return this;
    }

    public SlidingRootNavBuilder withContentClickableWhenMenuOpened(boolean clickable) {
        isContentClickableWhenMenuOpened = clickable;
        return this;
    }

    public SlidingRootNavBuilder withDragDistance(int vp) {
        return withDragDistancePx(dpToPx(vp));
    }

    public SlidingRootNavBuilder withDragDistancePx(int px) {
        dragDistance = px;
        return this;
    }

    public SlidingRootNavBuilder withOffsetDistance(int vp) {
        return withOffsetDistancePx(dpToPx(vp));
    }

    public SlidingRootNavBuilder withOffsetDistancePx(int px) {
        offsetDistance = px;
        return this;
    }

    public SlidingRootNavBuilder withRootViewScale(float scale) {
        layoutScale = scale;
        return this;
    }

    public SlidingRootNavBuilder addDragListener(DragListener dragListener) {
        dragListeners.add(dragListener);
        return this;
    }

    public SlidingRootNavBuilder addDragStateListener(DragStateListener dragStateListener) {
        dragStateListeners.add(dragStateListener);
        return this;
    }

    private int dpToPx(int dp) {
        return Math.round(AttrHelper.getDensity(ability) * dp);
    }
}
